package baijia

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"math"
	"net/http"
	"net/url"
	"strconv"
	"time"

	"code.whyyou.me/jiyue/goutils"
	bjhArticle "publish/pub-lib/baijia/bjh-model"
	"publish/pub-lib/utils"
	"fmt"
	"github.com/astaxie/beego"
	"github.com/kataras/go-errors"
	"github.com/lauyoume/gohttp"
	"io"
	"io/ioutil"
	"mime/multipart"
)

var (
	default_useragent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36"
)

type BaijiaPlat struct {
	Host   string
	Cookie string
	AppId  string
}

func NewAppIdBaijiaPlat(appId string, ck string) *BaijiaPlat {
	return &BaijiaPlat{
		Host:   "http://baijiahao.baidu.com",
		Cookie: ck,
		AppId:  appId,
	}
}

func (p *BaijiaPlat) getReq(ul string, headers ...string) ([]byte, error) {
	req := gohttp.New()
	req.Get(ul).Timeout(60*time.Second).
		Set("User-Agent", default_useragent).
		Set("Cookie", p.Cookie).
		Set("Pragma", "no-cache").
		Set("Cache-Control", "no-cache")

	for i := 0; i < len(headers); i += 2 {
		req.Set(headers[i], headers[i+1])
	}

	body, _, err := goutils.GetBodyFromReq(req, http.StatusOK)
	if err != nil {
		return nil, err
	}

	return body, nil
}

func (p *BaijiaPlat) postReq(ul string, typ string, data interface{}, headers ...string) ([]byte, error) {
	req := gohttp.New()
	req.Post(ul).Type(typ).Timeout(60*time.Second).
		Set("User-Agent", default_useragent).
		Set("Cookie", p.Cookie).
		Set("Pragma", "no-cache").
		Set("Cache-Control", "no-cache").
		Send(data)

	for i := 0; i < len(headers); i += 2 {
		req.Set(headers[i], headers[i+1])
	}

	body, _, err := goutils.GetBodyFromReq(req, http.StatusOK)
	if err != nil {
		return nil, err
	}
	return body, nil
}

func (p *BaijiaPlat) postFromCode(ul string, data string, headers ...string) ([]byte, int, error) {
	req := gohttp.New()
	req.Post(ul).Send(data).Timeout(60*time.Second).
		Set("User-Agent", default_useragent).
		Set("X-Requested-With", "XMLHttpRequest").
		Set("Cookie", p.Cookie).
		Set("Pragma", "no-cache").
		Set("Cache-Control", "no-cache").
		Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
	body, code, err := goutils.GetBodyFromReq(req, http.StatusOK)
	if err != nil {
		return nil, code, err
	}
	return body, code, nil
}

func (p *BaijiaPlat) post(ul string, params map[string]string) ([]byte, error) {
	req := utils.Post(ul)

	req.Header("User-Agent", default_useragent)
	req.Header("X-Requested-With", "XMLHttpRequest")
	req.Header("Cookie", p.Cookie)
	req.Header("Pragma", "no-cache")
	req.Header("Cache-Control", "no-cache")
	req.Header("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")

	for k, v := range params {
		req.Param(k, v)
	}

	return req.Bytes()
}

func (p *BaijiaPlat) postFormData(ul string, b io.Reader, w *multipart.Writer) ([]byte, error) {
	req, err := http.NewRequest("POST", ul, b)
	if err != nil {
		return nil, err
	}

	req.Header.Set("User-Agent", default_useragent)
	req.Header.Set("X-Requested-With", "XMLHttpRequest")
	req.Header.Set("Cookie", p.Cookie)
	req.Header.Set("Pragma", "no-cache")
	req.Header.Set("Cache-Control", "no-cache")
	req.Header.Set("Content-Type", w.FormDataContentType())

	client := &http.Client{}
	res, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	if res.StatusCode != http.StatusOK {
		err = fmt.Errorf("bad status: %s", res.Status)
	}

	body, err := ioutil.ReadAll(res.Body)
	defer res.Body.Close()

	return body, nil
}

func (p *BaijiaPlat) url(v string) string {
	return p.Host + v
}

func (p *BaijiaPlat) preuploadVideo() (*PreUploadResult, error) {
	req := utils.Post(`http://rsbjh.baidu.com/builder/author/video/preuploadVideo`)
	req.Header("Cookie", p.Cookie)
	req.Header("Accept-Encoding", "gzip, deflate")
	req.Header("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36")

	req.Param(`app_id`, p.AppId)
	result := &PreUploadResult{}
	err := req.ToJSON(result)
	if err != nil {
		bytes, err := req.Bytes()
		beego.Error("Res :", string(bytes), " err:", err)
		return nil, err
	}
	if result.ErrorMsg != `preuploadVideo success` {
		return nil, errors.New("返回结果不符合预期 ：" + result.ErrorMsg)
	}
	return result, nil
}

func (p *BaijiaPlat) CheckCookie() error {
	req := utils.Post(`http://rsbjh.baidu.com/builder/author/video/preuploadVideo`)
	req.Header("Cookie", p.Cookie)
	req.Header("Accept-Encoding", "gzip, deflate")
	req.Header("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36")

	req.Param(`app_id`, p.AppId)
	result := &PreUploadResult{}
	err := req.ToJSON(result)
	if err != nil {
		bytes, err := req.Bytes()
		beego.Error("Res :", string(bytes), " err:", err)
		return err
	}
	if result.ErrorCode != 20000 {
		return errors.New("cookie 无效")
	}
	return nil
}

func (p *BaijiaPlat) uploadVideo(uploadKey string, reader io.ReadCloser, filename string, size int64, chunks int, chunk int) (*UploadResult, error) {
	req := utils.Post(`http://rsbjh.baidu.com/builder/author/video/uploadVideo`)
	req.Header("Cookie", p.Cookie)
	req.Header("Accept-Encoding", "gzip, deflate")
	req.Header("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36")

	req.Param("app_id", p.AppId)
	req.Param("timestamp", strconv.FormatInt(time.Now().Unix(), 10))
	req.Param("id", "WU_FILE_0")
	req.Param("name", filename)
	req.Param("type", "video/mp4")
	req.Param("lastModifiedDate", time.Now().String())
	req.Param("size", strconv.FormatInt(size, 10))
	req.Param("chunks", strconv.Itoa(chunks))
	req.Param("chunk", strconv.Itoa(chunk))
	req.Param("upload_key", uploadKey)
	req.PostFileByStream("file", filename, reader)

	result := UploadResult{}
	if err := req.ToJSON(&result); err != nil {
		bytes, err := req.Bytes()
		beego.Error("Res :", string(bytes), " err:", err)
		return nil, err
	}
	if result.ErrorCode != 20000 {
		return nil, errors.New("uploadVideo ErrorCode:" + strconv.Itoa(result.ErrorCode) + " Msg:" + result.ErrorMsg)
	}

	reqs := req.GetRequest()
	beego.Warning("ContentLengt:", reqs.ContentLength)

	return &result, nil
}

func (p *BaijiaPlat) compuploadVideo(uploadKey string, filename string, size int64, chunks int) (*UploadVideoResult, error) {
	req := utils.Post(`http://rsbjh.baidu.com/builder/author/video/compuploadVideo`)
	req.Header("Cookie", p.Cookie)
	req.Header("Accept-Encoding", "gzip, deflate")
	req.Header("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36")

	req.Param(`upload_key`, uploadKey)
	req.Param("chunks", strconv.Itoa(chunks))
	req.Param("name", filename)
	req.Param("app_id", p.AppId)
	req.Param("size", strconv.FormatInt(size, 10))
	req.Param("type", "video")

	result := UploadVideoResult{}
	err := req.ToJSON(&result)
	if err != nil {
		bytes, err := req.Bytes()
		beego.Error("Res :", string(bytes), " err:", err)
		return nil, err
	}
	if result.ErrorCode != 0 {
		return nil, errors.New("发布失败 ErrorCode:" + strconv.Itoa(result.ErrorCode))
	}
	return &result, nil
}

func (p *BaijiaPlat) uploadFile(reader io.ReadCloser, filename string) (string, error) {
	req := utils.Post(`http://baijiahao.baidu.com/builderinner/api/content/file/upload?fileapi` + strconv.FormatInt(time.Now().Unix(), 10))

	req.Header("Cookie", p.Cookie)
	req.Header("Accept-Encoding", "gzip, deflate")
	req.Header("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36")

	req.Param("_media", filename)
	req.PostFileByStream("media", filename, reader)
	fileResult := &UploadFileResult{}
	err := req.ToJSON(fileResult)
	if err != nil {
		bytes, err := req.Bytes()
		beego.Error("Res :", string(bytes), " err:", err)
		return "", err
	}

	if fileResult.ErrorMsg != "success" {
		return "", errors.New("uploadFile 返回信息不符合预期：" + fileResult.ErrorMsg)

	}
	return fileResult.Ret.BosUrl, nil
}

func (p *BaijiaPlat) publishVideo(title string, desc string, video []byte, vedioName string, videoSize int64, cover io.ReadCloser) error {
	preupload, err := p.preuploadVideo()
	if err != nil {
		return err
	}
	beego.Info("上传准备 完成")
	var upload *UploadVideoResult
	var coverUrl string

	if videoSize > 2*1024*1024 {
		chunks := int(math.Floor(float64(videoSize/(2*1024*1024)))) + 1
		for chunk := 0; chunk < int(chunks); chunk++ {
			var videoReader io.ReadCloser
			if chunk != int(chunks)-1 {
				beego.Warning("chunk:", chunk, " chunks:", chunks, " videoSize:", videoSize, " begin:", chunk*2*1024*1024, " end:", (chunk+1)*2*1024*1024)
				videoReader = ioutil.NopCloser(bytes.NewBuffer(video[chunk*2*1024*1024 : (chunk+1)*2*1024*1024]))
			} else {
				beego.Warning("chunk:", chunk, " chunks:", chunks, " videoSize:", videoSize, " begin:", chunk*2*1024*1024, " end:", (chunk+1)*2*1024*1024)
				videoReader = ioutil.NopCloser(bytes.NewBuffer(video[chunk*2*1024*1024 : len(video)-1]))
			}
			result, err := p.uploadVideo(preupload.UploadKey, videoReader, vedioName, videoSize, int(chunks), chunk)
			if err != nil {
				return err
			}
			beego.Info("分块上传 完成 chunk:", chunk, " result:", result)
		}
		upload, err = p.compuploadVideo(preupload.UploadKey, vedioName, videoSize, int(chunks))
		if err != nil {
			return err
		}
		beego.Info("分块上传 全部完成 chunks:", upload)

		coverUrl, err = p.uploadFile(cover, "bg.jpg")
		if err != nil {
			return err
		}
		beego.Info("上传封面完成 result:", coverUrl)

	} else {
		result, err := p.uploadVideo(preupload.UploadKey, ioutil.NopCloser(bytes.NewBuffer(video)), vedioName, videoSize, 1, 0)
		if err != nil {
			return err
		}
		beego.Info("不分块上传 uploadVideo 完成 :", result)

		upload, err = p.compuploadVideo(preupload.UploadKey, vedioName, videoSize, 1)
		if err != nil {
			return err
		}
		beego.Info("不分块上传 全部完成 :", upload)

		coverUrl, err = p.uploadFile(cover, "bg.jpg")
		if err != nil {
			return err
		}
		beego.Info("上传封面完成 result:", coverUrl)

	}

	p.titleCheck(title)

	req := utils.Post(`http://baijiahao.baidu.com/builder/author/article/publish`)

	req.Header("Cookie", p.Cookie)
	req.Header("Accept-Encoding", "gzip, deflate")
	req.Header("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36")

	req.Param(`type`, "video")
	req.Param("app_id", p.AppId)
	req.Param("title", title)
	req.Param("subtitle", "")
	req.Param("feed_cat", "1005")
	req.Param("feed_sub_cat", "2060")
	req.Param("cover_images", fmt.Sprintf(`[{"src":"%s"}]`, coverUrl))
	req.Param(`content`, fmt.Sprintf(`[{"title":"%s","desc":"%s","local":1,"mediaId":"%s"}]`, title, desc, upload.MediaId))
	req.Param("cover_layout", "one")
	req.Param("tag", "")
	req.Param("spd_info", `{"goods_info":[]}`)
	req.Param("changeSpdData", "false")
	req.Param("original_status", "0")

	result := PublishVideoResult{}
	err = req.ToJSON(&result)
	if err != nil {
		beego.Error("PublishVideo Response Error:", err.Error())
		return err
	}

	if result.ErrorCode != 0 {
		beego.Error("发布视频失败: ", result)
		return errors.New(result.ErrorMsg)
	}

	beego.Info(`发布视频完成:`, result)
	return nil
}

func (p *BaijiaPlat) titleCheck(title string) (string,error){
	req := utils.Post(`http://baijiahao.baidu.com/builderinner/api/content/titleCheck`)

	req.Header("Cookie", p.Cookie)
	req.Header("Accept-Encoding", "gzip, deflate")
	req.Header("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36")

	req.Param("content", title)
	req.Param("app_id", p.AppId)

	return req.String()
}


const BJH_DUMP_IMAGE = "http://baijiahao.baidu.com/builderinner/api/content/file/image/dump?usage=content&is_waterlog=1&url=%s"
const BJH_UPLOAD_IMAGE = "http://rsbjh.baidu.com/builderinner/api/content/file/upload?encode=utf-8&is_waterlog=1&type=image&app_id=%s&usage=content"
const BJH_PUBLISH = "http://baijiahao.baidu.com/builder/author/article/publish"

func (p *BaijiaPlat) uploadBImage(info *multipart.FileHeader, f io.Reader) ([]byte, error) {

	var b bytes.Buffer
	var err error
	w := multipart.NewWriter(&b)

	var fw io.Writer

	if fw, err = w.CreateFormField("id"); err != nil {
		return nil, err
	}
	if _, err = fw.Write([]byte("WU_FILE_1")); err != nil {
		return nil, err
	}

	if fw, err = w.CreateFormField("name"); err != nil {
		return nil, err
	}

	if fw, err = w.CreateFormField("lastModifiedDate"); err != nil {
		return nil, err
	}

	if _, err = fw.Write([]byte(time.Now().String())); err != nil {
		return nil, err
	}

	if fw, err = w.CreateFormField("size"); err != nil {
		return nil, err
	}

	binary.Write(fw, binary.BigEndian, int32(info.Size))

	if fw, err = w.CreateFormFile("image", info.Filename); err != nil {
		return nil, err
	}

	if _, err = io.Copy(fw, f); err != nil {
		return nil, err
	}

	w.Close()

	ul := fmt.Sprintf(BJH_UPLOAD_IMAGE, p.AppId)

	body, err := p.postFormData(ul, &b, w)

	return body, err
}

func (p *BaijiaPlat) dumpImage(imageUrl string) (string, error) {
	data, err := p.getReq(fmt.Sprintf(BJH_DUMP_IMAGE, imageUrl))
	if err != nil {
		return "", err
	}
	dumpImage := bjhArticle.DumpImageResp{}
	err = json.Unmarshal(data, &dumpImage)
	if err != nil {
		return "", err
	}
	return dumpImage.Data.BosUrl, nil
}

func (p *BaijiaPlat) uploadImage(imageUrl string) (string, error) {
	data, err := p.getReq(fmt.Sprintf(BJH_UPLOAD_IMAGE, imageUrl))
	if err != nil {
		return "", err
	}
	dumpImage := bjhArticle.DumpImageResp{}
	err = json.Unmarshal(data, &dumpImage)
	if err != nil {
		return "", err
	}
	return dumpImage.Data.BosUrl, nil
}

func (p *BaijiaPlat) publish2(article bjhArticle.Article) ([]byte, error) {
	params, err := article.ToParams()
	if err != nil {
		return nil, err
	}
	return p.post(BJH_PUBLISH, params)
}

func (p *BaijiaPlat) publish(article bjhArticle.Article) ([]byte, error) {
	var sendData string
	var err error

	switch article.Type {
	case bjhArticle.BaiJia_ARTICLE_TYPE:
		sendData, err = article.ToUrlencoded()
	case bjhArticle.BaiJia_GALLERY_TYPE:
		sendData, err = article.ToGalleryUrlencoded()
	default:
		return nil, errors.New("not find the pub type ")
	}

	if err != nil {
		return nil, err
	}

	formVal, err := url.ParseQuery(sendData)
	if err != nil {
		return nil, err
	}
	for k, v := range formVal {
		fmt.Println(k, ":", v)
	}

	body, code, err := p.postFromCode(BJH_PUBLISH, sendData)

	beego.Info("Code", code, " Body:", string(body))

	if err != nil {
		return body, err
	}
	if code != 200 {
		return body, errors.New("return code not equal to 200")
	}
	return body, nil
}

